package init

import (
	"bufio"
	"context"
	"encoding/json"
	"fmt"
	"golang.org/x/net/websocket"
	"gorm.io/gorm"
	"log"
	"order_srv/basic/config"
	"order_srv/handler/models"
	"order_srv/untils"
	"os"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

// 全局上下文，用于管理导出任务
var exportContext = context.Background()

// getUserIP 获取用户IP地址
// 注意：在实际应用中，可能需要从请求上下文中获取真实的IP地址
func getUserIP() string {
	// 这里简化实现，实际应用中应该从请求中获取
	return "127.0.0.1"
}

// getUserAgent 获取用户代理信息
// 注意：在实际应用中，可能需要从请求上下文中获取真实的用户代理
func getUserAgent() string {
	// 这里简化实现，实际应用中应该从请求中获取
	return "ExportService/1.0"
}

// 启动导出任务消费者
func StartExportConsumer() {
	mq := NewRabbitMQSimple("order_export_queue")
	defer mq.Destory()

	// 接收消息
	msgs, err := mq.ConsumeSimple()
	if err != nil {
		log.Fatalf("启动导出任务消费者失败: %v", err)
	}

	forever := make(chan bool)
	go func() {
		for d := range msgs {
			log.Printf("收到导出任务: %s", d.Body)
			var taskMsg map[string]interface{}
			if err := json.Unmarshal(d.Body, &taskMsg); err != nil {
				log.Printf("解析任务失败: %v", err)
				continue
			}

			// 处理导出任务
			handleExportTask(taskMsg)
			d.Ack(false) // 手动确认消息已处理
		}
	}()

	log.Println("导出任务消费者已启动")
	<-forever
}

// tryWebSocketPush 尝试通过WebSocket推送消息
func tryWebSocketPush(userId uint32, taskId, message string) bool {
	if wsConn, exists := getWebSocketConnection(userId); exists {
		notifyMsg := map[string]interface{}{
			"type":      "export_task",
			"task_id":   taskId,
			"message":   message,
			"timestamp": time.Now().Unix(),
		}
		data, err := json.Marshal(notifyMsg)
		if err != nil {
			log.Printf("序列化通知消息失败: %v", err)
			return false
		}

		// 使用websocket库的正确方法发送消息
		if err := websocket.Message.Send(wsConn, string(data)); err == nil {
			log.Printf("用户%d的任务%s已通过WebSocket通知", userId, taskId)
			return true
		}
		log.Printf("用户%d的任务%s无法通过WebSocket通知: %v", userId, taskId, err)
	}
	return false
}

// SaveOfflineMessage 保存离线消息到数据库（可导出的公共函数）
func SaveOfflineMessage(userId uint32, taskId, message string) {
	saveOfflineMessage(userId, taskId, message)
}

// saveOfflineMessage 保存离线消息到数据库（内部实现）
func saveOfflineMessage(userId uint32, taskId, message string) {
	// 保存离线消息（同步写入数据库）
	// 由于RelatedId现在是int64类型，需要处理UUID格式的taskId
	// 这里使用一个临时方案：将UUID的前16位转换为整数
	var relatedId int64 = 1 // 设置一个非零默认值，确保数据库不会报错
	if len(taskId) > 0 {
		// 实际应用中可能需要更好的转换策略或调整数据库设计
		// 这里简单地使用一个固定值
		relatedId = 1
	}

	msg := &models.UserMessage{
		UserId:    userId,
		Title:     "导出任务通知",
		Content:   message,
		RelatedId: relatedId,
		MsgType:   3, // 3表示导出任务通知
		IsRead:    false,
		CreatedAt: time.Now(),
		// 将ReadAt设置为time.Time的零值的指针形式，这样在GORM中会被转换为NULL
		ReadAt: time.Time{},
	}

	// 尝试配置的最大重试次数保存离线消息
	// 使用Select方法排除ReadAt字段，这样GORM就不会尝试插入这个字段
	// 由于数据库中ReadAt字段允许NULL，这样就不会出现缺少默认值的错误
	success := false
	retryCount := 0
	maxRetries := ExportConfig.MaxRetryCount
	var lastErr error

	for retryCount < maxRetries {
		retryCount++
		if err := config.DB.Select("user_id", "title", "content", "related_id", "msg_type", "is_read", "created_at").Create(msg).Error; err != nil {
			lastErr = err
			log.Printf("第%d次保存离线消息失败: %v", retryCount, err)
			// 重试间隔递增
			time.Sleep(time.Duration(retryCount*500) * time.Millisecond)
		} else {
			success = true
			break
		}
	}

	if success {
		log.Printf("用户%d的任务%s已保存离线消息", userId, taskId)
	} else {
		log.Printf("多次尝试保存离线消息失败，最后错误: %v", lastErr)
		// 在日志中记录消息内容，以便后续排查
		log.Printf("未能保存的离线消息内容 - 用户ID: %d, 任务ID: %s, 消息: %s", userId, taskId, message)
	}
}

// 处理导出任务的核心逻辑
func handleExportTask(taskMsg map[string]interface{}) {
	taskID := taskMsg["task_id"].(string)
	userID := uint32(taskMsg["user_id"].(float64))

	// 记录操作开始日志
	operationParams, _ := json.Marshal(taskMsg)
	opLog := &models.OperationLog{
		UserID:      uint64(userID),
		Operation:   "订单导出",
		Resource:    "订单数据",
		Params:      string(operationParams),
		Result:      true,
		Description: "开始执行订单导出任务",
	}
	if err := opLog.Create(); err != nil {
		log.Printf("创建操作日志失败: %v", err)
	}

	// 权限控制：只有管理员才能导出订单数据
	// 查询用户信息
	user := &models.User{Id: uint64(userID)}
	if err := user.GetUserById(); err != nil {
		log.Printf("查询用户信息失败: %v", err)
		// 记录异常日志
		exceptionLog := &models.ExceptionLog{
			UserID:      uint64(userID),
			Module:      "订单导出",
			Exception:   "权限验证失败",
			Message:     fmt.Sprintf("查询用户信息失败: %v", err),
			RequestData: string(operationParams),
			IPAddress:   getUserIP(),    // 记录IP地址
			UserAgent:   getUserAgent(), // 记录用户代理
		}
		exceptionLog.Create()
		return
	}

	// 检查用户角色，只有管理员（UserRole=3）才能导出数据
	if user.UserRole != 3 {
		log.Printf("用户%d (角色%d) 没有权限导出订单数据", userID, user.UserRole)
		// 更新任务状态为失败
		task := &models.ExportTask{TaskId: taskID}
		if err := config.DB.Where("task_id = ?", taskID).First(task).Error; err == nil {
			task.UpdateStatus(uint8(3), "权限不足，只有管理员才能导出订单数据")
		}
		// 记录异常日志
		exceptionLog := &models.ExceptionLog{
			UserID:      uint64(userID),
			Module:      "订单导出",
			Exception:   "权限不足",
			Message:     "只有管理员才能导出订单数据",
			RequestData: string(operationParams),
			IPAddress:   getUserIP(),
			UserAgent:   getUserAgent(),
		}
		exceptionLog.Create()
		return
	}

	// 更新操作日志的用户角色和用户名
	opLog.UserRole = user.UserRole
	opLog.Username = user.NickName
	opLog.IPAddress = getUserIP()
	opLog.UserAgent = getUserAgent()
	if err := config.DB.Model(&models.OperationLog{}).Where("id = ?", opLog.ID).Updates(opLog).Error; err != nil {
		log.Printf("更新操作日志失败: %v", err)
	}

	// 1. 查询任务记录
	var task models.ExportTask
	if err := config.DB.Where("task_id = ?", taskID).First(&task).Error; err != nil {
		log.Printf("查询任务失败: %v", err)
		// 记录异常日志
		exceptionLog := &models.ExceptionLog{
			UserID:      uint64(userID),
			Module:      "订单导出",
			Exception:   "数据库查询失败",
			Message:     fmt.Sprintf("查询任务记录失败: %v", err),
			RequestData: string(operationParams),
			IPAddress:   getUserIP(),
			UserAgent:   getUserAgent(),
		}
		exceptionLog.Create()
	} else {
		// 2. 更新任务状态为处理中
		if err := task.UpdateStatus(uint8(1), "开始处理导出任务"); err != nil {
			log.Printf("更新任务状态为处理中失败: %v", err)
		}
	}

	// 尝试3次重试
	retryCount := 0
	maxRetries := 3
	for retryCount < maxRetries {
		retryCount++
		time.Sleep(2 * time.Second) // 等待2秒后重试
		if err := config.DB.Where("task_id = ?", taskID).First(&task).Error; err == nil {
			break
		}
	}
	if task.Id == 0 {
		log.Printf("多次尝试后仍无法查询到任务记录，任务ID: %s", taskID)
		return
	}

	// 2. 更新任务状态为处理中
	err := task.UpdateStatus(uint8(1), "")
	if err != nil {
		log.Printf("更新任务状态失败: %v", err)
	}

	// 3. 解析时间和订单类型参数
	startTimeStr := taskMsg["start_time"].(string)
	endTimeStr := taskMsg["end_time"].(string)
	// 正确解析订单类型，先转换为int64再转为int8，避免浮点数精度问题
	orderTypeFloat := taskMsg["order_type"].(float64)
	orderType := int8(int64(orderTypeFloat))

	// 解析时间，支持多种格式
	startTime, err := time.Parse(time.RFC3339, startTimeStr)
	if err != nil {
		// 尝试其他格式
		if startTime, err = time.Parse("2006-01-02 15:04:05", startTimeStr); err != nil {
			log.Printf("解析开始时间失败: %v", err)
			task.UpdateStatus(uint8(3), "解析开始时间失败: "+err.Error())
			return
		}
	}

	endTime, err := time.Parse(time.RFC3339, endTimeStr)
	if err != nil {
		// 尝试其他格式
		if endTime, err = time.Parse("2006-01-02 15:04:05", endTimeStr); err != nil {
			log.Printf("解析结束时间失败: %v", err)
			task.UpdateStatus(3, "解析结束时间失败: "+err.Error())
			return
		}
	}

	// 打印调试信息
	log.Printf("导出任务调试信息 - 任务ID: %s, 用户ID: %d", taskID, userID)
	log.Printf("时间范围: %s 到 %s", startTime.Format("2006-01-02 15:04:05"), endTime.Format("2006-01-02 15:04:05"))
	log.Printf("订单类型: %d", orderType)

	// 统计符合条件的总订单数
	var totalCount int64
	db := config.DB.Model(&models.Orders{}).
		Where("create_at BETWEEN ? AND ?", startTime, endTime).
		Where("order_type = ?", orderType)

	if err := db.Count(&totalCount).Error; err != nil {
		log.Printf("计算总记录数失败: %v", err)
		task.UpdateStatus(3, "计算总记录数失败: "+err.Error())
		return
	}

	log.Printf("符合条件的订单总数: %d", totalCount)

	// 检查是否超过最大导出记录数
	if ExportConfig.MaxExportRecords > 0 && totalCount > int64(ExportConfig.MaxExportRecords) {
		log.Printf("导出任务记录数超过限制: %d > %d", totalCount, ExportConfig.MaxExportRecords)
		task.UpdateStatus(3, fmt.Sprintf("导出记录数超过最大限制 %d 条", ExportConfig.MaxExportRecords))
		// 记录异常日志
		exceptionLog := &models.ExceptionLog{
			UserID:      uint64(userID),
			Module:      "订单导出",
			Exception:   "数据量过大",
			Message:     fmt.Sprintf("导出记录数 %d 超过最大限制 %d 条", totalCount, ExportConfig.MaxExportRecords),
			RequestData: string(operationParams),
		}
		exceptionLog.Create()
		return
	}

	task.TotalCount = uint32(totalCount)
	if err := task.UpdateStatus(1, ""); err != nil {
		log.Printf("更新任务状态失败: %v", err)
	}

	// 获取导出文件路径
	filePath := GetExportFilePath(taskID)
	file, err := os.Create(filePath)
	if err != nil {
		log.Printf("创建文件失败: %v", err)
		task.UpdateStatus(3, "创建文件失败: "+err.Error())
		return
	}
	defer file.Close()

	// 写入CSV表头（使用更友好的中文表头）
	head := "订单编号,用户ID,司机ID,订单类型,订单金额(元),创建时间,订单状态\n"
	if _, err := file.WriteString(head); err != nil {
		log.Printf("写入表头失败: %v", err)
		task.UpdateStatus(3, "写入文件失败: "+err.Error())
		return
	}

	// 创建Excel导出器
	excelExporter, err := untils.NewExcelExporter("order_export_" + taskID)
	if err != nil {
		log.Printf("创建Excel导出器失败: %v", err)
	} else {
		defer excelExporter.Close()
		
		// 创建订单数据工作表
		if err := excelExporter.CreateSheet("订单数据"); err != nil {
			log.Printf("创建工作表失败: %v", err)
		} else {
			// 设置活动工作表
			if err := excelExporter.SetActiveSheet("订单数据"); err != nil {
				log.Printf("设置活动工作表失败: %v", err)
			}
			
			// 写入Excel表头
			excelHeaders := []string{"订单编号", "用户ID", "司机ID", "订单类型", "订单金额(元)", "创建时间", "订单状态"}
			if err := excelExporter.WriteHeaders("订单数据", excelHeaders); err != nil {
				log.Printf("写入Excel表头失败: %v", err)
			}
		}
	}

	log.Printf("已写入CSV表头，准备开始写入数据 - 总记录数: %d", totalCount)

	// 分页参数，使用配置的批处理大小
	pageSize := ExportConfig.BatchSize
	page := 1
	// 分批处理，每次处理后刷新到磁盘，防止内存占用过高
	batchSize := ExportConfig.BatchSize
	// 使用缓冲写入器提高性能
	writer := bufio.NewWriter(file)
	defer writer.Flush()

	// 记录开始处理时间
	startTimeProcessing := time.Now()

	for {
		// 预估剩余时间
		if page > 1 {
			elapsed := time.Since(startTimeProcessing).Seconds()
			recordsProcessed := int64((page - 1) * pageSize)
			if recordsProcessed > totalCount {
				recordsProcessed = totalCount
			}
			rate := float64(recordsProcessed) / elapsed // 每秒处理记录数
			remainingRecords := totalCount - recordsProcessed
			remainingTime := float64(remainingRecords) / rate // 剩余秒数
			log.Printf("处理进度: %.2f%%, 估计剩余时间: %.0f秒",
				float64(recordsProcessed)/float64(totalCount)*100, remainingTime)
		}

		// 使用较小的结构体减少内存占用
		var orders []struct {
			OrderSn     string    `gorm:"column:order_sn"`
			UserId      uint32    `gorm:"column:user_id"`
			DriverId    uint32    `gorm:"column:driver_id"`
			OrderType   int8      `gorm:"column:order_type"`
			Price       float64   `gorm:"column:price"`
			CreateAt    time.Time `gorm:"column:create_at"`
			OrderStatus int8      `gorm:"column:order_status"`
		}
		offset := (page - 1) * pageSize

		// 分页查询，只查询需要的字段，提高性能
		log.Printf("查询第%d页数据，每页%d条，偏移量%d", page, pageSize, offset)
		result := config.DB.Table("orders").
			Select("order_sn, user_id, driver_id, order_type, price, create_at, order_status").
			Where("create_at BETWEEN ? AND ?", startTime, endTime).
			Where("order_type = ?", orderType).
			Limit(pageSize).
			Offset(offset).
			Find(&orders)

		if result.Error != nil {
			log.Printf("查询数据失败: %v", err)
			// 记录失败原因，任务标记为失败
			task.UpdateStatus(uint8(3), "查询数据失败: "+result.Error.Error())
			return
		}

		// 没有更多数据时退出循环
		if len(orders) == 0 {
			log.Printf("没有更多数据可导出，当前页码: %d", page)
			break
		}

		log.Printf("成功获取第%d页数据，记录数: %d", page, len(orders))

		// 流式写入文件，分批刷新
		for i, order := range orders {
			line := order.OrderSn + "," +
				strconv.Itoa(int(order.UserId)) + "," +
				strconv.Itoa(int(order.DriverId)) + "," +
				getOrderTypeText(order.OrderType) + "," +
				strconv.FormatFloat(order.Price, 'f', 2, 64) + "," +
				order.CreateAt.Format("2006-01-02 15:04:05") + "," +
				getOrderStatusText(order.OrderStatus) + "\n"

			if _, err := writer.WriteString(line); err != nil {
				log.Printf("写入文件失败: %v", err)
				task.UpdateStatus(3, "写入文件失败: "+err.Error())
				return
			}

			// 同时写入Excel文件
			if excelExporter != nil {
				rowData := []interface{}{
					order.OrderSn,
					order.UserId,
					order.DriverId,
					getOrderTypeText(order.OrderType),
					order.Price,
					order.CreateAt.Format("2006-01-02 15:04:05"),
					getOrderStatusText(order.OrderStatus),
				}
				
				// 计算Excel行号（从第2行开始，加上已处理的行数）
				excelRow := 2 + (page-1)*pageSize + i
				if err := excelExporter.WriteRow("订单数据", excelRow, rowData); err != nil {
					log.Printf("写入Excel数据失败: %v", err)
				}
			}

			// 根据配置的刷新间隔刷新文件
			if (i+1)%batchSize == 0 {
				if err := writer.Flush(); err != nil {
					log.Printf("刷新文件缓冲区失败: %v", err)
				}
				if err := file.Sync(); err != nil {
					log.Printf("同步文件到磁盘失败: %v", err)
				}
			}
		}

		// 确保当前批次数据刷新到磁盘
		if err := writer.Flush(); err != nil {
			log.Printf("刷新文件缓冲区失败: %v", err)
		}
		if err := file.Sync(); err != nil {
			log.Printf("同步文件到磁盘失败: %v", err)
		}

		// 更新进度
		processed := int64(page * pageSize)
		if processed > totalCount {
			processed = totalCount
		}
		// 批量更新进度，避免频繁数据库操作
		if processed%int64(pageSize*5) == 0 || processed == totalCount {
			if err := task.UpdateProgress(processed); err != nil {
				log.Printf("更新任务进度失败: %v", err)
			}
		}

		// 释放当前批次的内存
		orders = nil
		runtime.GC()

		page++
	}

	// 6. 完成导出，如果启用了加密功能，则进行文件加密
	encryptedSuccess := false
	if ExportConfig.EnableFileEncryption {
		// 使用上下文超时控制加密操作
		encryptCtx, cancel := context.WithTimeout(exportContext, 5*time.Minute) // 5分钟超时
		defer cancel()

		// 加密操作在单独的goroutine中执行
		encryptDone := make(chan struct{})
		go func() {
			defer close(encryptDone)

			// 创建加密器，最多重试3次
			var encryptor untils.FileEncryptor
			var err error
			encryptorRetry := 0
			for encryptorRetry < 3 {
				encryptorRetry++
				encryptor, err = untils.NewFileEncryptor(ExportConfig.EncryptionKey)
				if err == nil {
					break
				}
				log.Printf("第%d次创建文件加密器失败: %v", encryptorRetry, err)
				if encryptorRetry < 3 {
					time.Sleep(1 * time.Second) // 等待1秒后重试
				}
			}

			if err != nil {
				log.Printf("多次尝试创建文件加密器失败: %v", err)
				// 记录异常日志，但继续处理，不中断任务
				exceptionLog := &models.ExceptionLog{
					UserID:      uint64(userID),
					Module:      "订单导出",
					Exception:   "文件加密失败",
					Message:     fmt.Sprintf("创建文件加密器失败: %v", err),
					RequestData: string(operationParams),
					IPAddress:   getUserIP(),
					UserAgent:   getUserAgent(),
				}
				exceptionLog.Create()
				return
			}

			// 加密文件，最多重试3次
			encryptRetry := 0
			for encryptRetry < 3 {
				encryptRetry++
				select {
				case <-encryptCtx.Done():
					log.Printf("文件加密操作超时")
					return
				default:
					encryptedFilePath, err := encryptor.EncryptFile(filePath)
					if err == nil {
						log.Printf("导出文件已加密: %s", encryptedFilePath)
						filePath = encryptedFilePath // 更新文件路径为加密后的路径
						encryptedSuccess = true
						return
					}
					log.Printf("第%d次加密导出文件失败: %v", encryptRetry, err)
					if encryptRetry < 3 {
						time.Sleep(2 * time.Second) // 等待2秒后重试
					}
				}
			}

			if !encryptedSuccess {
				log.Printf("多次尝试加密导出文件失败: %v", err)
				// 记录异常日志，但继续处理，不中断任务
				exceptionLog := &models.ExceptionLog{
					UserID:      uint64(userID),
					Module:      "订单导出",
					Exception:   "文件加密失败",
					Message:     fmt.Sprintf("加密导出文件失败: %v", err),
					RequestData: string(operationParams),
					IPAddress:   getUserIP(),
					UserAgent:   getUserAgent(),
				}
				exceptionLog.Create()
			}
		}()

		// 等待加密完成或超时
		select {
		case <-encryptCtx.Done():
			log.Printf("文件加密操作超时，继续处理")
		case <-encryptDone:
			// 加密完成
		}
	}

	// 7. 保存Excel文件并更新任务状态为完成
	var allFilePaths []string
	allFilePaths = append(allFilePaths, filePath)
	
	if excelExporter != nil {
		// 设置Excel列宽
		excelExporter.SetColumnWidth("订单数据", "A", 20) // 订单编号
		excelExporter.SetColumnWidth("订单数据", "B", 10) // 用户ID
		excelExporter.SetColumnWidth("订单数据", "C", 10) // 司机ID
		excelExporter.SetColumnWidth("订单数据", "D", 12) // 订单类型
		excelExporter.SetColumnWidth("订单数据", "E", 15) // 订单金额
		excelExporter.SetColumnWidth("订单数据", "F", 20) // 创建时间
		excelExporter.SetColumnWidth("订单数据", "G", 12) // 订单状态
		
		// 保存Excel文件
		if err := excelExporter.Save(); err != nil {
			log.Printf("保存Excel文件失败: %v", err)
		} else {
			excelFilePath := excelExporter.GetFilePath()
			allFilePaths = append(allFilePaths, excelFilePath)
			log.Printf("Excel文件保存成功: %s", excelFilePath)
		}
	}
	
	// 更新任务文件路径
	task.FilePath = strings.Join(allFilePaths, ",")
	// 增加重试机制，确保状态更新成功
	success := false
	statusRetryCount := 0
	maxStatusRetries := 5
	var statusLastErr error

	// 创建带超时的上下文
	updateCtx, cancel := context.WithTimeout(exportContext, 30*time.Second)
	defer cancel()

	for statusRetryCount < maxStatusRetries {
		select {
		case <-updateCtx.Done():
			log.Printf("更新任务状态超时")
			break
		default:
			statusRetryCount++
			if err := task.UpdateStatus(uint8(2), ""); err != nil {
				statusLastErr = err
				log.Printf("第%d次更新任务状态为完成失败: %v", statusRetryCount, err)
				// 重试间隔递增，使用指数退避算法
				time.Sleep(time.Duration(statusRetryCount*1000) * time.Millisecond)
			} else {
				success = true
				log.Printf("任务状态更新为完成成功: 任务ID=%s, 文件路径=%s", task.TaskId, task.FilePath)
				break
			}
		}
	}

	if !success {
		log.Printf("多次尝试更新任务状态为完成失败，最后错误: %v", statusLastErr)
		// 记录异常日志
		exceptionLog := &models.ExceptionLog{
			UserID:      uint64(userID),
			Module:      "订单导出",
			Exception:   "任务更新失败",
			Message:     fmt.Sprintf("多次尝试更新任务状态为完成失败: %v", statusLastErr),
			RequestData: string(operationParams),
			IPAddress:   getUserIP(),
			UserAgent:   getUserAgent(),
		}
		exceptionLog.Create()
	}

	// 记录导出完成日志
	totalTime := time.Since(startTimeProcessing).Seconds()
	log.Printf("导出任务完成: 用户ID=%d, 任务ID=%s, 记录数=%d, 文件路径=%s, 总耗时=%.2f秒, 平均速度=%.2f条/秒",
		task.UserId, task.TaskId, totalCount, filePath, totalTime, float64(totalCount)/totalTime)

	// 更新操作日志，使用事务确保数据一致性
	if err := config.DB.Transaction(func(tx *gorm.DB) error {
		return tx.Model(&models.OperationLog{}).Where("id = ?", opLog.ID).Updates(map[string]interface{}{
			"result":      success,
			"description": fmt.Sprintf("订单导出完成，共导出%d条记录，耗时%.2f秒", totalCount, totalTime),
		}).Error
	}); err != nil {
		log.Printf("更新操作日志失败: %v", err)
		// 单独记录日志更新失败的异常
		exceptionLog := &models.ExceptionLog{
			UserID:    uint64(userID),
			Module:    "订单导出",
			Exception: "日志更新失败",
			Message:   fmt.Sprintf("更新操作日志失败: %v", err),
		}
		exceptionLog.Create()
	}

	// 8. 通知用户（可调用现有WebSocket或消息推送逻辑）
	// 使用单独的goroutine发送通知，避免阻塞主流程
	go func() {
		// 构建通知消息
		notifyMsg := "订单数据导出完成"
		if ExportConfig.EnableFileEncryption && encryptedSuccess {
			notifyMsg += "，文件已加密"
		}
		notifyMsg += "，您可以下载导出文件"

		// 通知用户
		notifyUser(userID, taskID, notifyMsg)
	}()
}

// 获取订单类型的文本描述
func getOrderTypeText(orderType int8) string {
	// 与orders.go中的订单类型定义保持一致
	typeMap := map[int8]string{
		1: "实时单",
		2: "预约单",
		3: "接机单",
		4: "送机单",
	}
	if text, ok := typeMap[orderType]; ok {
		return text
	}
	return strconv.Itoa(int(orderType))
}

// 获取订单状态的文本描述
func getOrderStatusText(status int8) string {
	statusMap := map[int8]string{
		0: "待支付",
		1: "待接单",
		2: "已接单",
		3: "进行中",
		4: "已完成",
		5: "已取消",
		6: "已退款",
	}
	if text, ok := statusMap[status]; ok {
		return text
	}
	return strconv.Itoa(int(status))
}

// notifyUser 通知用户导出任务状态
// 使用带超时控制的异步通知机制，优先通过WebSocket推送，失败则保存为离线消息
func notifyUser(userId uint32, taskId, message string) {
	// 使用带超时的上下文
	notifyCtx, cancel := context.WithTimeout(exportContext, 10*time.Second)
	defer cancel()

	// 创建通知任务
	notifyChan := make(chan error)
	go func() {
		// 1. 尝试通过WebSocket实时推送
		if success := tryWebSocketPush(userId, taskId, message); success {
			notifyChan <- nil
			return
		}

		// 记录WebSocket推送失败
		log.Printf("WebSocket推送失败，尝试保存离线消息: 用户ID=%d, 任务ID=%s", userId, taskId)

		// 2. WebSocket推送失败，保存离线消息
		saveOfflineMessage(userId, taskId, message)
		notifyChan <- nil
	}()

	// 等待通知完成或超时
	select {
	case <-notifyCtx.Done():
		log.Printf("通知用户超时: 用户ID=%d, 任务ID=%s", userId, taskId)
		// 记录异常日志
		exceptionLog := &models.ExceptionLog{
			UserID:    uint64(userId),
			Module:    "订单导出通知",
			Exception: "通知超时",
			Message:   fmt.Sprintf("通知用户%d任务%s状态超时", userId, taskId),
		}
		exceptionLog.Create()
	case err := <-notifyChan:
		if err != nil {
			log.Printf("通知用户失败: %v", err)
			// 记录异常日志
			exceptionLog := &models.ExceptionLog{
				UserID:    uint64(userId),
				Module:    "订单导出通知",
				Exception: "通知失败",
				Message:   fmt.Sprintf("通知用户%d任务%s状态失败: %v", userId, taskId, err),
			}
			exceptionLog.Create()
		}
	}
}

// 获取用户的WebSocket连接
// 从全局WebSocket管理器中查找指定用户的活跃连接
func getWebSocketConnection(userId uint32) (*websocket.Conn, bool) {
	// 检查WebSocket管理器是否已初始化
	if websocketManager == nil {
		log.Printf("WebSocket管理器未初始化")
		return nil, false
	}

	// 加锁保护并发访问
	websocketManager.mu.RLock()
	defer websocketManager.mu.RUnlock()

	// 从连接映射中查找用户连接
	conn, exists := websocketManager.connections[userId]
	if !exists {
		log.Printf("用户%d没有活跃的WebSocket连接", userId)
		return nil, false
	}

	// 验证连接状态
	if conn == nil {
		log.Printf("用户%d的WebSocket连接已关闭", userId)
		// 清理无效连接
		go func() {
			websocketManager.mu.Lock()
			delete(websocketManager.connections, userId)
			websocketManager.mu.Unlock()
		}()
		return nil, false
	}

	return conn, true
}

// WebSocket连接管理器（通常在项目中单独实现）
// 这里补充必要的类型定义以确保代码可正常工作
var websocketManager *WebSocketManager

type WebSocketManager struct {
	mu          sync.RWMutex
	connections map[uint32]*websocket.Conn // 用户ID到连接的映射
}

// 初始化WebSocket管理器
func InitWebSocketManager() {
	websocketManager = &WebSocketManager{
		connections: make(map[uint32]*websocket.Conn),
	}
	log.Println("WebSocket管理器已初始化")
}

// 添加WebSocket连接
func AddWebSocketConnection(userId uint32, conn *websocket.Conn) {
	if websocketManager == nil {
		InitWebSocketManager()
	}

	websocketManager.mu.Lock()
	websocketManager.connections[userId] = conn
	websocketManager.mu.Unlock()
	log.Printf("已添加用户%d的WebSocket连接", userId)
}

// 移除WebSocket连接
func RemoveWebSocketConnection(userId uint32) {
	if websocketManager == nil {
		return
	}

	websocketManager.mu.Lock()
	delete(websocketManager.connections, userId)
	websocketManager.mu.Unlock()
	log.Printf("已移除用户%d的WebSocket连接", userId)
}
